<template>
    <div class="wrapper" ref="wrapper">
        <div class="item" :style="{ opacity: item.opacity, transform: 'translate(' + item.x + 'px, ' + item.y + 'px)', width: item.width + 'px' }" v-for="(item, index) of list">
            <img class="img" :src="item.url" :style="{ height: item.imgHeight + 'px' }" @load="handleLoaded">
            <span class="text" ref="card">{{ item.shopName }} {{ item.text }}</span>
        </div>
    </div>

    <div class="loading" v-if="loading">加载中...</div>
    <div class="loading" style="color: red;" v-if="list.length === count">完成！</div>
</template>

<script setup>
import { ref, onMounted, nextTick } from 'vue'

const width = ref(0)
const count = ref(50)

const list = ref([])
const card = ref([])
const wrapper = ref(null)

let num = 0
let map = {}
const gap = 20

onMounted(async () => {
    num = getNum(wrapper.value.clientWidth)

    width.value = (wrapper.value.clientWidth - ((num - 1) * gap)) / num

    setMap()

    await setWaterfallFlow()

    // 监听尺寸更新
    // const ro = new ResizeObserver(handleSizeChange)

    // ro.observe(wrapper.value)

    window.addEventListener('scroll', handleScroll)
    window.addEventListener('resize', handleSizeChange)
})

const setWaterfallFlow = async (isAdd = false) => {
    const res = await getList()
    const data = await Promise.all(res.map(async (item) => {
        const { w, h } = await getRect(item.url)

        return {
            ... item,

            ratio: w / h,
            imgHeight: width.value * h / w
        }
    }))
    const length = list.value.length
    const arr = data.map((item, index) => {
        Object.assign(item, {
            width: width.value,
            x: 0,
            y: 0,

            text: index + 1 + length
        })

        return item
    })

    if (isAdd) {
        list.value = [... list.value, ... arr]
    } else {
        list.value = arr
    }

    await nextTick()

    list.value.forEach((item, index) => {
        if (index > length - 1) {
            item.height = card.value[index].offsetHeight + item.imgHeight + 20 + 20
        }
    })

    setTransform()
}

const handleLoaded = (e) => {
    e.target.classList.add('loaded')
}

const getNum = (width) => {
    let num

    if (width >= 1600) {
        num = 5
    }

    if (width < 1600 && width >= 1300) {
        num = 4
    }

    if (width < 1300 && width >= 1000) {
        num = 3
    }

    if (width < 1000) {
        num = 2
    }

    return num
}

const setMap = () => {
    let i = 0

    map = {}

    while(i < num) {
        map[Math.trunc(i * width.value + i * gap)] = 0
        i++
    }
}

let n = 1
const getList = async () => {
    const url = `http://localhost:1314/api/list?num=${n++}`
    const res = await fetch(url)
    const json = await res.json()

    return json.data.map((item) => {
        item.url = item.attachmentInfoList[0]

        return item
    })
}

const getRect = (url) => {
    return new Promise((resolve, reject) => {
        const img = new Image()

        img.src = url
        img.onload = () => {
            resolve({
                w: img.width,
                h: img.height
            })
        }
    })
}

const setTransform = () => {
    list.value.forEach((item, index) => {
        const i = index % num

        if (index < num) {
            item.x = Math.trunc(i * width.value + i * gap)
            item.y = 0

            map[item.x] = item.height
        } else {
            const { x, y } = getMinBox()

            item.x = x
            item.y = y + gap

            map[item.x] += gap + item.height
        }
    })

    const obj = getMinBox(true)

    wrapper.value.style.height = obj.y + 20 + 'px'
}

const getMinBox = (isMax = false) => {
    const min = Math.min(... Object.values(map))
    const max = Math.max(... Object.values(map))

    const x = Object.keys(map).find((key) => map[key] === (isMax ? max : min))
    const y = map[x]

    return {
        x,
        y
    }
}

// const handleSizeChange = (entries) => {
//     for (let entry of entries) {

//         setTimeout(() => {
//             num = getNum(entry.contentRect.width)

//             width.value = (entry.contentRect.width - ((num - 1) * gap)) / num

//             setMap()

//             list.value.forEach((item, index) => {
//                 item.width = width.value
//                 item.height = card.value[index].offsetHeight
//             })

//             setTransform()
//         }, 50)
//     }
// }

const handleSizeChange = () => {
    const contentRectWidth = wrapper.value.offsetWidth

    num = getNum(contentRectWidth)

    width.value = (contentRectWidth - ((num - 1) * gap)) / num

    setMap()

    list.value.forEach((item, index) => {
        item.imgHeight = width.value / item.ratio
        item.width = width.value
    })

    setTimeout(() => {
        list.value.forEach((item, index) => {
            item.height = item.imgHeight + 40 + card.value[index].offsetHeight
        })

        setTransform()
    })
}

const loading = ref(false)
const handleScroll = async () => {
    if (list.value.length >= count.value) {
        return
    }

    if (loading.value) {
        return
    }

    if (document.scrollingElement.scrollHeight - innerHeight - document.scrollingElement.scrollTop <= 50) {
        loading.value = true

        await setWaterfallFlow(true)

        loading.value = false
    }
}

const add = () => {
    const arr = Array.from({length: 10}).fill(0).map((item, index) => ({
        width: width.value,
        height: Math.ceil(Math.random() * 300 + 100),
        x: 0,
        y: 0,

        index: index + list.value.length,
        text: index + 1 + list.value.length,
        bg: getBg()
    }))

    const length = list.value.length

    list.value = [... list.value, ... arr]

    // 从新增数据处开始处理
    setTransform(length % num === 0 ? length : length - (length % num))
}

const rafThrottle = (fn) => {
    let lock = false

    return function (...args) {
        window.requestAnimationFrame(() => {
            if (lock) {
                return
            }

            lock = true
            fn.apply(this, args)
            lock = false
        })
    }
}

</script>

<style scoped>
button {
    width: 150px;
    margin: 20px auto;
    text-align: center;
    color: #000;
    cursor: pointer;
    border-radius: 20px;
    line-height: 50px;
}
.loading {
    padding: 20px 0;
    font-size: 24px;
    text-align: center;
}
.wrapper {
    position: relative;
/*    overflow: hidden;*/
    box-sizing: border-box;
}
.item {
    padding-bottom: 20px;
    position: absolute;
    top: 0;
    left: 0;
    font-size: 20px;
/*    opacity: 0;*/
    overflow: hidden;
    border-radius: 15px;
    box-shadow: 0 0 20px rgba(0, 0, 0, .3);
    background-color: #F5F5F5;
}
.img {
    display: block;
    width: 100%;
    margin-bottom: 20px;
    filter: blur(18px);
}
.img.loaded {
    filter: blur(0);
}
.text {
    display: block;
    padding: 0 20px;
}
</style>
